package com.boot.security.service.database.impl;

import com.boot.persistence.service.ServiceImpl;
import com.boot.security.mapper.ResourceMapper;
import com.boot.security.model.po.Resource;
import com.boot.security.model.vo.SourceKey;
import com.boot.security.service.authorization.AccessSource;
import com.boot.security.service.authorization.AccessSourceClusterService;
import com.boot.security.service.database.ResourceService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author 霜寒 <1621856595@qq.com>
 * @date 2020/2/18 17:38
 **/
@Slf4j
@Service
public class ResourceServiceImpl extends ServiceImpl<Long, Resource, ResourceMapper> implements ResourceService {

    private final AccessSource accessSource;

    private final AccessSourceClusterService[] accessSourceHandlers;

    public ResourceServiceImpl(ResourceMapper mapper, AccessSource accessSource, @Autowired(required = false) AccessSourceClusterService[] accessSourceHandlers) {
        super(mapper);
        this.accessSource = accessSource;
        if (accessSourceHandlers == null) {
            log.info("There is no IAccessSourceHandler for updating AccessSource, the application is standalone!");
        }
        this.accessSourceHandlers = accessSourceHandlers;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean save(Resource model) {
        boolean result = super.save(model);
        if (result) {
            SourceKey sourceKey = accessSource.saveOne(model.getId());
            for (AccessSourceClusterService accessSourceHandler : accessSourceHandlers) {
                accessSourceHandler.saveOne(sourceKey, model.getId());
            }
        }
        return result;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateById(Resource model) {
        Resource resource = mapper.selectById(model.getId());
        boolean result = super.updateById(model);
        if (result) {
            SourceKey sourceKey;
            accessSource.getWriteLock().lock();
            try {
                sourceKey = accessSource.updateOne(resource);
            } finally {
                accessSource.getWriteLock().unlock();
            }
            for (AccessSourceClusterService accessSourceHandler : accessSourceHandlers) {
                accessSourceHandler.updateOne(sourceKey, resource);
            }
        }
        return result;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean removeById(Long id) {
        Resource resource = mapper.selectById(id);
        boolean result = super.removeById(id);
        if (result) {
            SourceKey sourceKey = accessSource.removeOne(resource);
            for (AccessSourceClusterService accessSourceHandler : accessSourceHandlers) {
                accessSourceHandler.removeOne(sourceKey, resource);
            }
        }
        return result;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean removeByIds(Collection<? extends Long> idList) {
        List<Resource> resources = mapper.selectBatchIds(idList);
        boolean result = super.updateBatchById(idList.stream().map(id -> Resource.builder()
                        .id(id)
                        .build())
                .collect(Collectors.toList()));
        result = result && super.removeByIds(idList);
        if (result) {
            SourceKey sourceKey = accessSource.removeMulti(resources);
            for (AccessSourceClusterService accessSourceHandler : accessSourceHandlers) {
                accessSourceHandler.removeMulti(sourceKey, resources);
            }
        }
        return result;
    }

    @Override
    public Resource createModel() {
        return new Resource();
    }
}
